package com.tools.common.object;

import java.lang.reflect.MalformedParameterizedTypeException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Objects;
import java.util.StringJoiner;

/**
 * sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 类的代替实现
 * */
@Note("sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 类的代替实现")
public final class ParameterizedTypeImpl implements ParameterizedType {

    @Note("泛型类型数组")
    private final Type[] actualTypeArguments;

    @Note("原生类型")
    private final Class<?> rawType;

    @Note("若是内部类，此为拥有者类型")
    private final Type ownerType;

    /* ************************************************************************************************************
     *
     *      构造器
     *
     * ************************************************************************************************************
     * */

    private ParameterizedTypeImpl(Class<?> rawType, Type[] actualTypeArguments, Type ownerType) {
        this.actualTypeArguments = actualTypeArguments;
        this.rawType             = rawType;
        this.ownerType = (ownerType != null) ? ownerType : rawType.getDeclaringClass();
    }

    public static ParameterizedTypeImpl make(Class<?> rawType, Type[] actualTypeArguments, Type ownerType) {
        return new ParameterizedTypeImpl(rawType, actualTypeArguments, ownerType);
    }


    /* ************************************************************************************************************
     *
     *      其他方法
     *
     * ************************************************************************************************************
     * */

    @Override
    public Type[] getActualTypeArguments() {
        return actualTypeArguments.clone();
    }

    @Override
    public Type getRawType() {
        return rawType;
    }

    @Override
    public Type getOwnerType() {
        return ownerType;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof ParameterizedType) {
            ParameterizedType that = (ParameterizedType) o;
            if(this == that) return true;
            Type thatOwner = that.getOwnerType();
            Type thatRawType = that.getRawType();
            return Objects.equals(ownerType, thatOwner) &&
                    Objects.equals(rawType, thatRawType) &&
                    Arrays.equals(actualTypeArguments, that.getActualTypeArguments());
        }
        return false;
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(actualTypeArguments) ^
                        Objects.hashCode(ownerType) ^
                        Objects.hashCode(rawType);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (ownerType != null) {
            sb.append(ownerType.getTypeName()).append("$");
            if (ownerType instanceof ParameterizedTypeImpl) {
                ParameterizedTypeImpl type = (ParameterizedTypeImpl) ownerType;
                sb.append(rawType.getName().replace( type.rawType.getName() + "$", ""));
            } else {
                sb.append(rawType.getSimpleName());
            }
        } else {
            sb.append(rawType.getName());
        }
        if (actualTypeArguments != null) {
            StringJoiner sj = new StringJoiner(", ", "<", ">");
            sj.setEmptyValue("");
            for(Type t: actualTypeArguments) {
                sj.add(t.getTypeName());
            }
            sb.append(sj);
        }
        return sb.toString();
    }
}
